#pragma once
#include "PDTCodeGraph.h"
#include "PDTExp.h"
#include "PDTVar.h"
#include "PDTVecConst.h"
#include "PDTNumConst.h"
#include "PDTSequenceVar.h"

class PDTSequenceAccessNum :  public PDTExpNum, public PDTVar, public TraverseIfc <PDTSequenceAccessNum, PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTSequenceAccessNum)
public:
    PDTSequenceAccessNum(PDTCodeGraph & CG, PDTSequenceVar & left, PDTExpNum & right) :
        PDTExpNum(CG), PDTVar((left.getSequence())->name) {
            addChild(left.getNode());
            addChild(&right);

            left.addParent(this);
            right.addParent(this);

            CG.addVar(this);
        }

    NodeTypeEnum getType() {return PDTSequenceAccessNum_Em;}
    
    virtual bool isSequenceAccessNum() {
    	return true;
    }

    virtual bool isVar() {
        return true;
    }

    virtual PDTNode* getNode() {
        return this;
    }

    PDTSequence * getLeft() const {
        PDTSequenceVar * seqVar = (PDTSequenceVar*)children[0];
        PDTSequence * seq = seqVar->getSequence();
        return seq;
        //return dynamic_cast<PDTSequence*> (children[0]);
    }

    PDTSequenceVar * getLeftVar() const {
        return dynamic_cast <PDTSequenceVar *>(children[0]);
    }

    PDTExpNum* getRight() const {
        return dynamic_cast<PDTExpNum*> (children[1]);
    }
    
    PDTVar * getIndexVar() {
    	if (children[1]->isVar())
    		return dynamic_cast<PDTVar*>(children[1]);
        return NULL;
    }

    unsigned int getSequenceSize() const {
        PDTSequence * seq = getLeft();
        return seq->getSize();
    }

    virtual bool eval(PDTCodeGraph &CG)
    {
    	getRight()->eval(CG); //compute index
    	PDTNode * idxVal = getRight()->getNValue();
    	int index = (dynamic_cast<PDTNumConstInt*>(idxVal))->getValue(); //int index
   	 	PDTNode * element = getLeft()->getElement(index); // get Value at index
   	 	if (getLeft()->isElementInt()) //If Int, initialize nValue to int
		{
   	 		int intElement = (dynamic_cast<PDTNumConstInt*>(element))->getValue();
   	 		nValue = new PDTNumConstInt(CG, intElement);
			return true;
		}

		if (getLeft()->isElementBoolean())
		{
			int boolElement = (dynamic_cast<PDTBoolConst*>(element))->getValue();
			nValue = new PDTBoolConst(CG, boolElement);
			return true;
		}

   	 	if ( nValue == NULL ){
   	 		cerr << "Warning: array access " << getLeft()->name << "[" << index << " ]" << " is not available." << endl;
            return false;
   	 	}
        return true;
   	}


    virtual stringstream & toString(stringstream & ss) const {
        getLeft()->toString(ss);
        ss << "at (";
        getRight()->toString(ss);
        ss << ")";
        return ss;
    }
};

class PDTSequenceAccessBool :  public PDTExpBool, public PDTVar, public TraverseIfc <PDTSequenceAccessBool, PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTSequenceAccessBool)
public:
    PDTSequenceAccessBool(PDTCodeGraph & CG, PDTSequenceVar & left, PDTExpNum & right) :
        PDTExpBool(CG), PDTVar(left.getSequence()->name) {
            addChild(left.getNode());
            addChild(&right);

            left.addParent(this);
            right.addParent(this);

            CG.addVar(this);
        }

    NodeTypeEnum getType() {return PDTSequenceAccessBool_Em;}

    virtual bool isSequenceAccessBool() {
    	return true;
    }

    virtual PDTNode* getNode() {
        return this;
    }

    PDTSequenceVar * getLeftVar() const {
        return dynamic_cast <PDTSequenceVar *>(children[0]);
    }

    PDTSequence * getLeft() const {
        PDTSequenceVar * seqVar = (PDTSequenceVar*)children[0];
        PDTSequence * seq = seqVar->getSequence();
        return seq;
        //return dynamic_cast<PDTSequence*> (children[0]);
    }

    PDTExpNum * getRight() const {
        return dynamic_cast<PDTExpNum*> (children[1]);
    }

    virtual bool eval(PDTCodeGraph &CG)
    {
    	getRight()->eval(CG); //compute index
    	PDTNode * idxVal = getRight()->getNValue();
    	int index = (dynamic_cast<PDTNumConstInt*>(idxVal))->getValue(); //int index
   	 	PDTNode * element = getLeft()->getElement(index); // get Value at index
   	 	if (getLeft()->isElementInt()) //If Int, initialize nValue to int
		{
   	 		int intElement = (dynamic_cast<PDTNumConstInt*>(element))->getValue();
   	 		nValue = new PDTNumConstInt(CG, intElement);
			return true;
		}

		if (getLeft()->isElementBoolean())
		{
			int boolElement = (dynamic_cast<PDTBoolConst*>(element))->getValue();
			nValue = new PDTBoolConst(CG, boolElement);
			return true;
		}

   	 	if ( nValue == NULL ){
   	 		cerr << "Warning: array access " << getLeft()->name << "[" << index << " ]" << " is not available." << endl;
   	 	}
        return true;
   	}

    virtual stringstream & toString(stringstream & ss) const {
        getLeft()->toString(ss);
        ss << "at (";
        getRight()->toString(ss);
        ss << ")";
        return ss;
    }
};
